package com.xinguan.aspect;

import com.google.gson.Gson;
import com.xinguan.annotation.ControllerEndPoint;
import com.xinguan.entity.TbLog;
import com.xinguan.entity.TbUser;
import com.xinguan.service.TbLogService;
import com.xinguan.utils.AddressUtil;
import com.xinguan.utils.IPUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;

/**
 * @author 郭经伟
 * @Date 2021/7/24
 * 系统日志切面
 **/
@Slf4j
@Aspect
@Component
public class ControllerEndPointAspect {


    private long startTime;

    private final TbLog sysLog = new TbLog();

    @Autowired
    private TbLogService logService;

    @Pointcut("@annotation(com.xinguan.annotation.ControllerEndPoint))")
    public void pointcut() {
    }

    /**
     * 环绕通知
     *
     * @param joinPoint
     * @return
     */
    @Around("pointcut()")
    public Object saveSysLog(ProceedingJoinPoint joinPoint) throws Throwable {

        Object result = null;
        // 开始时间
        startTime = System.currentTimeMillis();
        /**
         * 1.joinPoint.getArgs():获取带参方法的参数
         * 2.joinPoint.getTarget():.获取他们的目标对象信息
         * 3..joinPoint.getSignature():(signature是信号,标识的意思):获取被增强的方法相关信息
         */
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        ControllerEndPoint controllerEndPointAnnotation = method.getDeclaredAnnotation(ControllerEndPoint.class);
        // 可以获得注解中对应的信息
        if (controllerEndPointAnnotation != null) {
            String operation = controllerEndPointAnnotation.operation();
            // 操作描述
            sysLog.setOperation(operation);
        }
        // 获得增强方法中的方法参数
        Object[] args = joinPoint.getArgs();
        // 获取方法参数名
        LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
        String[] parameterNames = u.getParameterNames(method);
        sysLog.setParams("paramName:" + Arrays.toString(parameterNames) + ",args:" + Arrays.toString(args));
        // 请求的IP
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ipAddr = IPUtil.getIpAddr(request);
        sysLog.setIp(ipAddr);
        // 地理位置
        sysLog.setLocation(AddressUtil.getCityInfo(ipAddr));
        // 操作人
        TbUser user = (TbUser) SecurityUtils.getSubject().getPrincipal();
        sysLog.setUsername(user.getUsername());
        // 添加时间
        sysLog.setCreateTime(new Date());
        // 执行目标方法
        result = joinPoint.proceed();
        // 请求方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = methodSignature.getName();
        sysLog.setMethod(className + "." + methodName + "()\n"
                + "\nresponse:" + postHandle(result));
        // 执行耗时
        sysLog.setTime(System.currentTimeMillis() - startTime);

        // 保存系统操作日志
        logService.save(sysLog);
        return result;
    }

    /**
     * 返回数据
     *
     * @param retVal
     * @return
     */
    private String postHandle(Object retVal) {
        if (null == retVal) {
            return "";
        }
        Gson gson = new Gson();
        return gson.toJson(retVal);
    }

}
